/*
 * MIT License
 *
 * Copyright (c) 2023 北京凯特伟业科技有限公司
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.je.workflow.service.usertask.impl;

import com.google.common.base.Strings;
import com.je.bpm.core.model.BpmnModel;
import com.je.bpm.core.model.FlowElement;
import com.je.bpm.core.model.config.CounterSignPassTypeEnum;
import com.je.bpm.core.model.task.*;
import com.je.bpm.engine.HistoryService;
import com.je.bpm.engine.ProcessEngine;
import com.je.bpm.engine.TaskService;
import com.je.bpm.engine.delegate.event.ActivitiCountersignedOpinionType;
import com.je.bpm.engine.history.*;
import com.je.bpm.engine.impl.HistoricVariableInstanceQueryProperty;
import com.je.bpm.engine.impl.bpmn.behavior.MultiInstanceActivityBehavior;
import com.je.bpm.engine.impl.identity.Authentication;
import com.je.bpm.engine.impl.persistence.entity.CommentEntity;
import com.je.bpm.engine.impl.persistence.entity.CommentEntityImpl;
import com.je.bpm.engine.impl.persistence.entity.PassRoundEntity;
import com.je.bpm.engine.task.Comment;
import com.je.bpm.engine.task.IdentityLinkType;
import com.je.common.base.DynaBean;
import com.je.common.base.mapper.query.NativeQuery;
import com.je.common.base.result.BaseRespResult;
import com.je.common.base.service.CommonService;
import com.je.common.base.service.MetaResourceService;
import com.je.common.base.service.MetaService;
import com.je.common.base.util.DateUtils;
import com.je.common.base.util.SecurityUserHolder;
import com.je.common.base.workflow.vo.CirculationHistoryVo;
import com.je.common.base.workflow.vo.CirculationHistoryVoStateEnum;
import com.je.common.base.workflow.vo.CommentVo;
import com.je.common.base.workflow.vo.OperationTypeEnum;
import com.je.rbac.rpc.MetaRbacRpcServiceImpl;
import com.je.workflow.service.user.WorkFlowUserService;
import com.je.workflow.service.usertask.CurrentUserParam;
import com.je.workflow.service.usertask.CurrentUserServiceFactory;
import com.je.workflow.service.usertask.CurrentUserTaskEnum;
import com.je.workflow.service.usertask.CurrentUserTaskService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.DateFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
public class CurrentUserTaskServiceImpl implements CurrentUserTaskService {
    @Autowired
    private MetaService metaService;
    @Autowired
    private MetaRbacRpcServiceImpl metaRbacRpcService;
    @Autowired
    private MetaResourceService metaResourceService;
    @Autowired
    private HistoryService historyService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private ProcessEngine processEngine;
    @Autowired
    private WorkFlowUserService workFlowUserService;
    @Autowired
    private CommonService commonService;

    private static final DateFormat nodeDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

    private static final DateFormat commentDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private Map<String, String> userInfo = new HashMap<>();

    @Override
    public Map<String, Object> getCurrentUserTaskList(CurrentUserTaskEnum findCurrentTaskEnum, CurrentUserParam upcomingParam) {
        return CurrentUserServiceFactory.getService(findCurrentTaskEnum).getTask(upcomingParam);
    }

    @Override
    public List<Map<String, String>> getInitiateList(String sort, String name) {
        List<Map<String, String>> result = new ArrayList<>();
        List<Map<String, Object>> ddInfos = getWFDDInfos();
        String code = "";
        if (!Strings.isNullOrEmpty(sort)) {
            NativeQuery nativeQuery = NativeQuery.build();
            nativeQuery.tableCode("JE_CORE_DICTIONARYITEM");
            nativeQuery.eq("JE_CORE_DICTIONARYITEM_ID", sort);
            List<DynaBean> list = metaResourceService.selectByNativeQuery(nativeQuery);
            DynaBean dynaBean = list.get(0);
            code = dynaBean.getStr("DICTIONARYITEM_ITEMCODE");
        }
        NativeQuery rbacNativeQuery = NativeQuery.build().tableCode("je_core_menu").in("MENU_FUNCTYPE", "grid", "form");
        if (Strings.isNullOrEmpty(code)) {
            List<String> codes = new ArrayList<>();
            for (Map<String, Object> child : ddInfos) {
                if (child.get("JE_CORE_DICTIONARYITEM_ID").equals(WfProcessManagerImpl.ROOT_ID)) {
                    continue;
                }
                codes.add((String) child.get("DICTIONARYITEM_ITEMCODE"));
            }
            rbacNativeQuery.in("MENU_WFMODULE", codes);
        } else {
            rbacNativeQuery.eq("MENU_WFMODULE", code);
        }

        if (!Strings.isNullOrEmpty(name)) {
            rbacNativeQuery.apply(String.format(" and ( MENU_WFNAME like '%s' OR MENU_MENUNAME like '%s' ) ", "%" + name + "%", "%" + name + "%"));
        }

        List<DynaBean> menus = metaRbacRpcService.selectByNativeQuery(rbacNativeQuery);
        for (DynaBean menu : menus) {
            Map<String, String> menuMap = new HashMap<>();
            if (Strings.isNullOrEmpty(menu.getStr("MENU_NODEINFO"))) {
                continue;
            }
            String[] nodeInfo = menu.getStr("MENU_NODEINFO").split(",");
            menuMap.put("funcCode", nodeInfo[0]);
            menuMap.put("funcName", menu.getStr("MENU_MENUNAME"));
            if (!Strings.isNullOrEmpty(menu.getStr("MENU_WFNAME"))) {
                menuMap.put("name", menu.getStr("MENU_WFNAME"));
            } else {
                menuMap.put("name", menu.getStr("MENU_MENUNAME"));
            }
            menuMap.put("iconColor", menu.getStr("MENU_BGCOLOR"));
            menuMap.put("iconCls", menu.getStr("MENU_ICON"));

            String MENU_WFMODULE = menu.getStr("MENU_WFMODULE");
            for (Map<String, Object> map : ddInfos) {
                if (map.get("DICTIONARYITEM_ITEMCODE").equals(MENU_WFMODULE)) {
                    menuMap.put("ddId", (String) map.get("JE_CORE_DICTIONARYITEM_ID"));
                    menuMap.put("ddCode", (String) map.get("DICTIONARYITEM_ITEMCODE"));
                    menuMap.put("ddText", (String) map.get("DICTIONARYITEM_ITEMNAME"));
                    menuMap.put("ddTextEn", (String) map.get("DICTIONARYITEM_ITEMNAME_EN"));
                    break;
                }
            }
            if (menuMap.get("ddId") == null || menuMap.get("ddCode") == null) {
                continue;
            }
            result.add(menuMap);
        }
        return result;
    }

    /**
     * 获取流程分类字典
     *
     * @return
     */
    private List<Map<String, Object>> getWFDDInfos() {
        NativeQuery nativeQuery = NativeQuery.build();
        nativeQuery.tableCode("JE_CORE_DICTIONARYITEM");
        nativeQuery.eq("DICTIONARYITEM_DICTIONARY_ID", WfProcessManagerImpl.DICTIONARY_ID);
        return metaResourceService.selectMapByNativeQuery(nativeQuery);
    }

    @Override
    public Map<String, Long> getBadges(CurrentUserParam upcomingParam) {
        Map<String, Long> badges = new HashMap<>();
        for (CurrentUserTaskEnum findCurrentTaskEnum : CurrentUserTaskEnum.values()) {
            badges.put(findCurrentTaskEnum.toString(), CurrentUserServiceFactory.getService(findCurrentTaskEnum).getBadge(upcomingParam));
        }
        return badges;
    }

    @Override
    public Boolean collect(String piid, String status) {
        metaService.executeSql(String.format("DELETE FROM JE_WORKFLOW_COLLECT WHERE COLLECT_PIID='%s' and SY_CREATEUSERID='%s'",
                piid, Authentication.getAuthenticatedUser().getDeptId()));
        if (status.equals("0")) {
            metaService.executeSql(String.format("DELETE FROM JE_WORKFLOW_COLLECT WHERE COLLECT_PIID='%s' and SY_CREATEUSERID='%s'",
                    piid, Authentication.getAuthenticatedUser().getDeptId()));
        } else {
            DynaBean dynaBean = new DynaBean("JE_WORKFLOW_COLLECT", true);
            dynaBean.setStr("COLLECT_PIID", piid);
            commonService.buildModelCreateInfo(dynaBean);
            dynaBean.setStr("SY_CREATEUSERID", Authentication.getAuthenticatedUser().getDeptId());
            metaService.insert(dynaBean);
        }
        return true;
    }

    @Override
    public Boolean delay(String id, String status) {
        DynaBean dynaBean = metaService.selectOneByPk("JE_WORKFLOW_RN_TASK", id);
        dynaBean.setStr("TASK_DELAY", status);
        metaService.update(dynaBean);
        return true;
    }

    @Override
    public List<CirculationHistoryVo> getCirculationHistory(String beanId) {
        userInfo = new HashMap<>();
        List<CirculationHistoryVo> list = new ArrayList<>();
        List<HistoricProcessInstance> procinst = historyService.createHistoricProcessInstanceQuery().processInstanceBusinessKey(beanId).list();
        if (procinst.size() == 0) {
            return list;
        }
        String piid = procinst.get(0).getId();
        String pdid = procinst.get(0).getProcessDefinitionId();
        LinkedHashMap<String, Map<String, String>> nodeInfos = new LinkedHashMap<String, Map<String, String>>();
        //拿到所有的流程实例，根据时间正序放到nodeInfos
        List<HistoricActivityInstance> actinst = historyService.createHistoricActivityInstanceQuery().processInstanceId(piid).
                orderByHistoricActivityInstanceStartTime().asc().list();
        for (HistoricActivityInstance historicActivityInstance : actinst) {
            //去除开始节点和结束节点
            if (historicActivityInstance.getActivityType().equals("startEvent") || historicActivityInstance.getActivityType().equals("endEvent")) {
                continue;
            }
            Map<String, String> node = new HashMap<>();
            node.put("name", historicActivityInstance.getActivityName());
            node.put("type", historicActivityInstance.getActivityType());
            node.put("id", historicActivityInstance.getActivityId());
            node.put("startTime", nodeDateFormat.format(historicActivityInstance.getStartTime()));
            if (historicActivityInstance.getEndTime() == null) {
                node.put("endTime", nodeDateFormat.format(new Date()));
            } else {
                node.put("endTime", nodeDateFormat.format(historicActivityInstance.getEndTime()));
            }
            nodeInfos.put(historicActivityInstance.getActivityId(), node);
        }
        BpmnModel bpmnModel = processEngine.getProcessEngineConfiguration().getRepositoryService().getBpmnModel(pdid);
        List<HistoricTaskInstance> tasks = historyService.createHistoricTaskInstanceQuery().processInstanceId(piid).
                orderByTaskCreateTime().asc().list();
        List<String> taskIds = new ArrayList<>();
        List<String> executionId = new ArrayList<>();
        for (HistoricTaskInstance historicTaskInstance : tasks) {
            //如果是多人节点或者会签节点
            if (bpmnModel.getFlowElement(historicTaskInstance.getTaskDefinitionKey()) instanceof KaiteMultiUserTask ||
                    bpmnModel.getFlowElement(historicTaskInstance.getTaskDefinitionKey()) instanceof KaiteCounterSignUserTask) {
                //如果已经执行过
                if (executionId.contains(historicTaskInstance.getExecutionId())) {
                    continue;
                }
            }
            if (taskIds.contains(historicTaskInstance.getId())) {
                continue;
            } else {
                executionId.add(historicTaskInstance.getExecutionId());
                taskIds.add(historicTaskInstance.getId());
            }
            List<Comment> commentEntities = taskService.getTaskComments(historicTaskInstance.getId());
            //委托人或者转办人
            Boolean candidate = false;
            for (Comment comment : commentEntities) {
                if (comment.getType().equals(Comment.CANDIDATE_USER_ID)) {
                    candidate = true;
                }
                if (comment.getType().equals(Comment.TRANSFER_USER_ID)) {
                    candidate = true;
                }
            }

            Map<String, String> nodeInfo = nodeInfos.get(historicTaskInstance.getTaskDefinitionKey());
            CirculationHistoryVo circulationHistoryVo = CirculationHistoryVo.build(nodeInfo.get("type"));
            getCandidateUser(commentEntities, nodeInfo, circulationHistoryVo, historicTaskInstance, list);
            if (candidate) {
                continue;
            }

            if (Strings.isNullOrEmpty(nodeInfo.get("name"))) {
                circulationHistoryVo.setNodeName(KaiteTaskCategoryEnum.getName(nodeInfo.get("type")));
            } else {
                circulationHistoryVo.setNodeName(nodeInfo.get("name"));
            }
            circulationHistoryVo.setNodeId(nodeInfo.get("id"));
            if (nodeInfo.get("type").equals(KaiteTaskCategoryEnum.KAITE_COUNTERSIGN_USERTASK.getType())) {
                circulationHistoryVo.setIsShowTitle("1");
            }
            String nodeComment = "";

            if (circulationHistoryVo.getIsShowTitle().equals("1")) {
                circulationHistoryVo.setTitle(getCountersignedTitle(historicTaskInstance.getTaskDefinitionKey(), bpmnModel));
            }
            circulationHistoryVo.setStartTime(nodeDateFormat.format(DateUtils.getDate(nodeInfo.get("startTime"), "yyyy-MM-dd HH:mm:ss.SSS")));
            //设置意见和处理人信息
            circulationHistoryVo.addComments(getComments(tasks, historicTaskInstance, circulationHistoryVo, nodeComment,
                    taskIds, bpmnModel));
            circulationHistoryVo.setState(CirculationHistoryVoStateEnum.NORMAL);
            list.add(circulationHistoryVo);
        }

        List<String> keys = new ArrayList<>();
        for (String key : nodeInfos.keySet()) {
            if (nodeInfos.get(key).get("type").equals("inclusiveGateway")) {
                keys.add(key);
            }
        }

        if (keys.size() == 0) {
            return buildResultList(list);
        }
        for (int j = 0; j < keys.size(); j++) {
            Map<String, String> node = nodeInfos.get(keys.get(j));
            CirculationHistoryVo newCirculationHistoryVo = CirculationHistoryVo.build(node.get("type"));
            if (Strings.isNullOrEmpty(node.get("name"))) {
                newCirculationHistoryVo.setNodeName(KaiteTaskCategoryEnum.getName(node.get("type")));
            } else {
                newCirculationHistoryVo.setNodeName(node.get("name"));
            }
            if (node.get("startTime") != null) {
                newCirculationHistoryVo.setStartTime(node.get("startTime"));
            }
            list.add(newCirculationHistoryVo);
        }

        CirculationHistoryVo[] arr = list.toArray(new CirculationHistoryVo[0]);
        for (int i = 0; i < arr.length - 1; i++) {//控制趟数
            //定义boolean类型变量,用来标识每趟是否存在交换值
            boolean flag = false;
            for (int j = 0; j < arr.length - 1 - i; j++) {//控制次数
                //比较相邻元素的值
                Date oneTime = DateUtils.getDate(arr[j].getStartTime(), "yyyy-MM-dd HH:mm:ss.SSS");
                Date twoTime = DateUtils.getDate(arr[j + 1].getStartTime(), "yyyy-MM-dd HH:mm:ss.SSS");
                if (oneTime.getTime() > twoTime.getTime()) {
                    CirculationHistoryVo t = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = t;
                    flag = true;//说明这趟存在交换值情况
                }
            }
            if (!flag) { //取反
                break;//若flag为false,表示此趟不存在交换值的情况,提前结束循环
            }
        }

        return buildResultList(Arrays.asList(arr));
    }

    public boolean hasCirculationInformation(String taskId, String userId) {
        // 在这里编写判断是否存在传阅信息的逻辑
        // 返回 true 表示存在传阅信息，返回 false 表示不存在传阅信息
        List<PassRoundEntity> list = processEngine.getProcessEngineConfiguration().getHistoryService().getPassRoundByTaskId(taskId, userId);
        if (list.size() > 0) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 设为已读
     *
     * @param pkId 主键
     * @return
     */
    @Override
    public BaseRespResult setAsRead(String pkId) {
        //如果主键为空 全部置为已读
        String accountDeptId = SecurityUserHolder.getCurrentAccount().getDeptId();
        if (Strings.isNullOrEmpty(pkId)) {
            metaService.executeSql("update JE_WORKFLOW_APPROVALNOTICE set APPROVALNOTICE_XXZT_CODE = '1' , APPROVALNOTICE_XXZT_NAME = '已读' where APPROVALNOTICE_TOUSER_ID = {0}", accountDeptId);
        } else {
            metaService.executeSql("update JE_WORKFLOW_APPROVALNOTICE set APPROVALNOTICE_XXZT_CODE = '1' , APPROVALNOTICE_XXZT_NAME = '已读' where JE_WORKFLOW_APPROVALNOTICE_ID = {0} and APPROVALNOTICE_TOUSER_ID = {1}", pkId, accountDeptId);
        }
        return null;
    }

    @Override
    public BaseRespResult getRreApprovNum(String userId, String deptId) {
        CurrentUserParam upcomingParam = CurrentUserParam.build("", "", "", "", null, CurrentUserTaskEnum.PI_PREAPPROV, "");
        Long badge = CurrentUserServiceFactory.getService(CurrentUserTaskEnum.PI_PREAPPROV).getBadge(upcomingParam);
        return BaseRespResult.successResult(badge);
    }


    private List<CirculationHistoryVo> buildResultList(List<CirculationHistoryVo> list) {
        for (CirculationHistoryVo vo : list) {
            if (!Strings.isNullOrEmpty(vo.getStartTime())) {
                vo.setStartTime(commentDateFormat.format(DateUtils.getDate(vo.getStartTime(), "yyyy-MM-dd HH:mm:ss.SSS")));
            }
            if (!Strings.isNullOrEmpty(vo.getEndTime())) {
                vo.setEndTime(commentDateFormat.format(DateUtils.getDate(vo.getEndTime(), "yyyy-MM-dd HH:mm:ss.SSS")));
            }
        }
        return list;
    }

    /**
     * 委托处理
     *
     * @param commentEntities
     * @param nodeInfo
     * @param circulationHistoryVo
     * @param historicTaskInstance
     * @param list
     */
    private void getCandidateUser(List<Comment> commentEntities, Map<String, String> nodeInfo, CirculationHistoryVo circulationHistoryVo,
                                  HistoricTaskInstance historicTaskInstance, List<CirculationHistoryVo> list) {
        int i = 0;
        for (Comment comment : commentEntities) {
            Date endTime = null;
            circulationHistoryVo = CirculationHistoryVo.build(nodeInfo.get("type"));
            if (Strings.isNullOrEmpty(nodeInfo.get("name"))) {
                circulationHistoryVo.setNodeName(KaiteTaskCategoryEnum.getName(nodeInfo.get("type")));
            } else {
                circulationHistoryVo.setNodeName(nodeInfo.get("name"));
            }
            if (comment.getType().equals(Comment.CANDIDATE_USER_ID)) {
                buildCandidateUserId(comment, circulationHistoryVo, list, historicTaskInstance, commentEntities);
            } else if (comment.getType().equals(Comment.TRANSFER_USER_ID)) {
                buildTransferUserId(comment, circulationHistoryVo, list, historicTaskInstance, commentEntities);
            } else if (comment.getType().equals(Comment.DELEGATE_TRANSFER_USER_ID)) {
                buildTurnToDoUserId(comment, circulationHistoryVo, list, historicTaskInstance, commentEntities);
            } else if (comment.getType().equals(Comment.DELEGATE_USER_ID)) {
                buildDelegateUserId(comment, circulationHistoryVo, list, historicTaskInstance, commentEntities);
            } else if (comment.getType().equals(Comment.CANCEL_CANDIDATE_USER_ID)) {
                buildCancelCandidateUserId(comment, circulationHistoryVo, list, historicTaskInstance, commentEntities);
            }
            i++;
        }
    }

    //委托人
    private void buildCandidateUserId(Comment comment, CirculationHistoryVo circulationHistoryVo, List<CirculationHistoryVo> list,
                                      HistoricTaskInstance historicTaskInstance, List<Comment> commentEntities) {
        List<String> users = new ArrayList<>();
        Date endTime = null;
        String userName = workFlowUserService.getUserNameById(comment.getUserId());
        users.add(userName);
        circulationHistoryVo.setAssignees(users);
        circulationHistoryVo.setState(CirculationHistoryVoStateEnum.NORMAL);
        circulationHistoryVo.setStartTime(nodeDateFormat.format(historicTaskInstance.getStartTime()));
        CommentVo commentVo = new CommentVo();
        if (historicTaskInstance.getEndTime() != null) {
            //是我处理的
            if (isLastComment(comment, commentEntities)) {
                endTime = historicTaskInstance.getEndTime();
                circulationHistoryVo.setEndTime(nodeDateFormat.format(historicTaskInstance.getEndTime()));
                for (Comment otherComment : commentEntities) {
                    if (otherComment.getType().equals(Comment.USER_COMMENT)) {
                        commentVo.setComment(otherComment.getFullMessage());
                    } else if (otherComment.getType().equals(Comment.NODE_TYPE)) {
                        circulationHistoryVo.setOperationType(comment.getFullMessage());
                    }
                }
                if (Strings.isNullOrEmpty(circulationHistoryVo.getOperationType())) {
                    circulationHistoryVo.setOperationType(OperationTypeEnum.PROCESSED.getShowName());
                }
            } else {
                //不是我处理，还有后续的撤销
                endTime = getNextDate(comment.getId(), commentEntities);
                circulationHistoryVo.setEndTime(nodeDateFormat.format(getNextDate(comment.getId(), commentEntities)));
                circulationHistoryVo.setOperationType(OperationTypeEnum.CANCEL_DELEGATE.getShowName());
                for (Comment commentEntity : commentEntities) {
                    if (commentEntity.getType().equals(Comment.NODE_TYPE)) {
                        if (commentEntity instanceof CommentEntityImpl) {
                            circulationHistoryVo.setOperationType(((CommentEntityImpl) commentEntity).getMessage());
                        }
                    }
                }
            }
        } else {
            if (isLastComment(comment, commentEntities)) {
                circulationHistoryVo.setOperationType(OperationTypeEnum.PROCESSING.getShowName());
                endTime = new Date();
            } else {
                //不是我处理，还有后续的撤销
                circulationHistoryVo.setEndTime(nodeDateFormat.format(getNextDate(comment.getId(), commentEntities)));
                circulationHistoryVo.setOperationType(OperationTypeEnum.CANCEL_DELEGATE.getShowName());
                for (Comment commentEntity : commentEntities) {
                    if (commentEntity.getType().equals(Comment.NODE_TYPE)) {
                        if (commentEntity instanceof CommentEntityImpl) {
                            circulationHistoryVo.setOperationType(((CommentEntityImpl) commentEntity).getMessage());
                        }
                    }
                }
                endTime = getNextDate(comment.getId(), commentEntities);
            }
        }
        circulationHistoryVo.setDurationInMillis(timeStampToDhms(endTime.getTime() -
                historicTaskInstance.getStartTime().getTime()));
        circulationHistoryVo.setOperationType(OperationTypeEnum.DELEGATE.getShowName());
        commentVo.setUserId(comment.getUserId());
        commentVo.setUserAvatar(workFlowUserService.getUserPhotoById(comment.getUserId()));
        commentVo.setUserName(userName);
        CommentEntity commentEntity = (CommentEntity) comment;
        commentVo.setComment(commentEntity.getMessage());
        commentVo.setEndTime(commentDateFormat.format(endTime));
        if (hasCirculationInformation(historicTaskInstance.getId(), commentVo.getUserId())) {
            commentVo.setHasCirculationInformation(true);
            commentVo.setTaskId(historicTaskInstance.getId());
        }
        circulationHistoryVo.addComment(commentVo);
        list.add(circulationHistoryVo);
    }

    //转办
    private void buildTransferUserId(Comment comment, CirculationHistoryVo circulationHistoryVo, List<CirculationHistoryVo> list,
                                     HistoricTaskInstance historicTaskInstance, List<Comment> commentEntities) {
        List<String> users = new ArrayList<>();
        Date endTime = null;
        String userName = workFlowUserService.getUserNameById(comment.getUserId());
        users.add(userName);
        circulationHistoryVo.setAssignees(users);
        circulationHistoryVo.setState(CirculationHistoryVoStateEnum.NORMAL);
        circulationHistoryVo.setStartTime(nodeDateFormat.format(historicTaskInstance.getStartTime()));
        CommentVo commentVo = new CommentVo();
        if (historicTaskInstance.getEndTime() != null) {
            //是我处理的
            if (isLastComment(comment, commentEntities)) {
                endTime = historicTaskInstance.getEndTime();
                circulationHistoryVo.setEndTime(nodeDateFormat.format(historicTaskInstance.getEndTime()));
                for (Comment otherComment : commentEntities) {
                    if (otherComment.getType().equals(Comment.USER_COMMENT)) {
                        commentVo.setComment(otherComment.getFullMessage());
                    } else if (otherComment.getType().equals(Comment.NODE_TYPE)) {
                        circulationHistoryVo.setOperationType(comment.getFullMessage());
                    }
                }
                if (Strings.isNullOrEmpty(circulationHistoryVo.getOperationType())) {
                    circulationHistoryVo.setOperationType(OperationTypeEnum.PROCESSED.getShowName());
                }
            } else {
                //不是我处理，还有后续的撤销
                endTime = getNextDate(comment.getId(), commentEntities);
                circulationHistoryVo.setEndTime(nodeDateFormat.format(getNextDate(comment.getId(), commentEntities)));
                circulationHistoryVo.setOperationType(OperationTypeEnum.TRANSFER.getShowName());
            }
        } else {
            if (isLastComment(comment, commentEntities)) {
                circulationHistoryVo.setOperationType(OperationTypeEnum.PROCESSING.getShowName());
                endTime = new Date();
            } else {
                //不是我处理，还有后续的撤销
                circulationHistoryVo.setEndTime(nodeDateFormat.format(getNextDate(comment.getId(), commentEntities)));
                circulationHistoryVo.setOperationType(OperationTypeEnum.TRANSFER.getShowName());
                endTime = getNextDate(comment.getId(), commentEntities);
            }
        }
        circulationHistoryVo.setDurationInMillis(timeStampToDhms(endTime.getTime() -
                historicTaskInstance.getStartTime().getTime()));
        circulationHistoryVo.setOperationType(OperationTypeEnum.TRANSFER.getShowName());
        commentVo.setUserId(comment.getUserId());
        commentVo.setUserAvatar(workFlowUserService.getUserPhotoById(comment.getUserId()));
        commentVo.setUserName(userName);
        CommentEntity commentEntity = (CommentEntity) comment;
        commentVo.setComment(commentEntity.getMessage());
        commentVo.setEndTime(commentDateFormat.format(endTime));
        circulationHistoryVo.addComment(commentVo);
        if (hasCirculationInformation(historicTaskInstance.getId(), commentVo.getUserId())) {
            commentVo.setHasCirculationInformation(true);
            commentVo.setTaskId(historicTaskInstance.getId());
        }
        list.add(circulationHistoryVo);
    }

    //被委托人
    private void buildDelegateUserId(Comment comment, CirculationHistoryVo circulationHistoryVo, List<CirculationHistoryVo> list,
                                     HistoricTaskInstance historicTaskInstance, List<Comment> commentEntities) {
        List<String> users = new ArrayList<>();
        String userName = workFlowUserService.getUserNameById(comment.getFullMessage());
        users.add(userName);
        circulationHistoryVo.setAssignees(users);
        CommentVo commentVo = new CommentVo();
        commentVo.setUserId(comment.getFullMessage());
        commentVo.setUserName(userName);
        commentVo.setUserAvatar(workFlowUserService.getUserPhotoById(comment.getFullMessage()));
        Date endTime = null;
        circulationHistoryVo.setStartTime(nodeDateFormat.format(comment.getTime()));
        if (historicTaskInstance.getEndTime() != null) {
            //是我处理的
            if (isLastComment(comment, commentEntities)) {
                endTime = historicTaskInstance.getEndTime();
                circulationHistoryVo.setEndTime(nodeDateFormat.format(historicTaskInstance.getEndTime()));
                for (Comment otherComment : commentEntities) {
                    if (otherComment.getType().equals(Comment.USER_COMMENT)) {
                        commentVo.setComment(otherComment.getFullMessage());
                    } else if (otherComment.getType().equals(Comment.NODE_TYPE)) {
                        circulationHistoryVo.setOperationType(otherComment.getFullMessage());
                    }
                }
                if (Strings.isNullOrEmpty(circulationHistoryVo.getOperationType())) {
                    circulationHistoryVo.setOperationType(OperationTypeEnum.PROCESSED.getShowName());
                }
            } else {
                //不是我处理，还有后续的撤销，还有驳回，退回操作
                endTime = getNextDate(comment.getId(), commentEntities);
                circulationHistoryVo.setOperationType(OperationTypeEnum.CANCEL_DELEGATE.getShowName());
                for (Comment commentEntity : commentEntities) {
                    if (commentEntity.getType().equals(Comment.NODE_TYPE)) {
                        if (commentEntity instanceof CommentEntityImpl) {
                            circulationHistoryVo.setOperationType(((CommentEntityImpl) commentEntity).getMessage());
                        }
                    }
                }
                circulationHistoryVo.setEndTime(nodeDateFormat.format(getNextDate(comment.getId(), commentEntities)));
                //添加意见，后续那个任务直接return
            }
        } else {
            if (isLastComment(comment, commentEntities)) {
                circulationHistoryVo.setOperationType(OperationTypeEnum.PROCESSING.getShowName());
                endTime = new Date();
            } else {
                //不是我处理，还有后续的撤销
                circulationHistoryVo.setEndTime(nodeDateFormat.format(getNextDate(comment.getId(), commentEntities)));
                circulationHistoryVo.setOperationType(OperationTypeEnum.CANCEL_DELEGATE.getShowName());
                for (Comment commentEntity : commentEntities) {
                    if (commentEntity.getType().equals(Comment.NODE_TYPE)) {
                        if (commentEntity instanceof CommentEntityImpl) {
                            circulationHistoryVo.setOperationType(((CommentEntityImpl) commentEntity).getMessage());
                        }
                    }
                }
                endTime = getNextDate(comment.getId(), commentEntities);
            }
        }
        commentVo.setEndTime(commentDateFormat.format(endTime));
        circulationHistoryVo.setState(CirculationHistoryVoStateEnum.NORMAL);
        circulationHistoryVo.setDurationInMillis(timeStampToDhms(endTime.getTime() - comment.getTime().getTime()));
        circulationHistoryVo.addComment(commentVo);
        if (hasCirculationInformation(historicTaskInstance.getId(), commentVo.getUserId())) {
            commentVo.setHasCirculationInformation(true);
            commentVo.setTaskId(historicTaskInstance.getId());
        }
        list.add(circulationHistoryVo);
    }

    //被转办
    private void buildTurnToDoUserId(Comment comment, CirculationHistoryVo circulationHistoryVo, List<CirculationHistoryVo> list,
                                     HistoricTaskInstance historicTaskInstance, List<Comment> commentEntities) {
        List<String> users = new ArrayList<>();
        String userName = workFlowUserService.getUserNameById(comment.getFullMessage());
        users.add(userName);
        circulationHistoryVo.setAssignees(users);
        CommentVo commentVo = new CommentVo();
        commentVo.setUserId(comment.getFullMessage());
        commentVo.setUserName(userName);
        commentVo.setUserAvatar(workFlowUserService.getUserPhotoById(comment.getFullMessage()));
        Date endTime = null;
        circulationHistoryVo.setStartTime(nodeDateFormat.format(comment.getTime()));
        if (historicTaskInstance.getEndTime() != null) {
            //是我处理的
            if (isLastComment(comment, commentEntities)) {
                endTime = historicTaskInstance.getEndTime();
                circulationHistoryVo.setEndTime(nodeDateFormat.format(historicTaskInstance.getEndTime()));
                for (Comment otherComment : commentEntities) {
                    if (otherComment.getType().equals(Comment.USER_COMMENT)) {
                        commentVo.setComment(otherComment.getFullMessage());
                    } else if (otherComment.getType().equals(Comment.NODE_TYPE)) {
                        circulationHistoryVo.setOperationType(otherComment.getFullMessage());
                    }
                }
                if (Strings.isNullOrEmpty(circulationHistoryVo.getOperationType())) {
                    circulationHistoryVo.setOperationType(OperationTypeEnum.PROCESSED.getShowName());
                }
            } else {
                //不是我处理，还有后续的撤销
                endTime = getNextDate(comment.getId(), commentEntities);
                circulationHistoryVo.setEndTime(nodeDateFormat.format(getNextDate(comment.getId(), commentEntities)));
                if (commentEntities.size() > 0) {
                    for (Comment otherComment : commentEntities) {
                        if (otherComment.getType().equals(Comment.NODE_TYPE)) {
                            circulationHistoryVo.setOperationType(otherComment.getFullMessage());
                        }
                        if (otherComment.getType().equals(Comment.USER_COMMENT)) {
                            commentVo.setComment(otherComment.getFullMessage());
                        }
                    }
                } else {
                    circulationHistoryVo.setOperationType(OperationTypeEnum.NONE.getShowName());
                }
            }
        } else {
            if (isLastComment(comment, commentEntities)) {
                circulationHistoryVo.setOperationType(OperationTypeEnum.PROCESSING.getShowName());
                endTime = new Date();
            } else {
                //不是我处理，还有后续的撤销
                circulationHistoryVo.setEndTime(nodeDateFormat.format(getNextDate(comment.getId(), commentEntities)));
                circulationHistoryVo.setOperationType(OperationTypeEnum.CANCEL_DELEGATE.getShowName());
                for (Comment commentEntity : commentEntities) {
                    if (commentEntity.getType().equals(Comment.NODE_TYPE)) {
                        if (commentEntity instanceof CommentEntityImpl) {
                            circulationHistoryVo.setOperationType(((CommentEntityImpl) commentEntity).getMessage());
                        }
                    }
                }
                endTime = getNextDate(comment.getId(), commentEntities);
            }
        }
        commentVo.setEndTime(commentDateFormat.format(endTime));
        circulationHistoryVo.setState(CirculationHistoryVoStateEnum.NORMAL);
        circulationHistoryVo.setDurationInMillis(timeStampToDhms(endTime.getTime() - comment.getTime().getTime()));
        circulationHistoryVo.addComment(commentVo);
        if (hasCirculationInformation(historicTaskInstance.getId(), commentVo.getUserId())) {
            commentVo.setHasCirculationInformation(true);
            commentVo.setTaskId(historicTaskInstance.getId());
        }
        list.add(circulationHistoryVo);
    }

    //撤销委托人
    private void buildCancelCandidateUserId(Comment comment, CirculationHistoryVo circulationHistoryVo, List<CirculationHistoryVo> list,
                                            HistoricTaskInstance historicTaskInstance, List<Comment> commentEntities) {
        List<String> users = new ArrayList<>();
        String userName = workFlowUserService.getUserNameById(comment.getFullMessage());
        users.add(userName);
        circulationHistoryVo.setAssignees(users);
        CommentVo commentVo = new CommentVo();
        commentVo.setUserId(comment.getFullMessage());
        commentVo.setUserName(userName);
        commentVo.setUserAvatar(workFlowUserService.getUserPhotoById(comment.getFullMessage()));
        Date endTime = null;
        circulationHistoryVo.setStartTime(nodeDateFormat.format(comment.getTime()));
        if (historicTaskInstance.getEndTime() != null) {
            //是我处理的
            if (isLastComment(comment, commentEntities)) {
                endTime = historicTaskInstance.getEndTime();
                circulationHistoryVo.setEndTime(nodeDateFormat.format(historicTaskInstance.getEndTime()));
                for (Comment otherComment : commentEntities) {
                    if (otherComment.getType().equals(Comment.USER_COMMENT)) {
                        commentVo.setComment(otherComment.getFullMessage());
                    } else if (otherComment.getType().equals(Comment.NODE_TYPE)) {
                        circulationHistoryVo.setOperationType(comment.getFullMessage());
                    }
                }
                if (Strings.isNullOrEmpty(circulationHistoryVo.getOperationType())) {
                    circulationHistoryVo.setOperationType(OperationTypeEnum.PROCESSED.getShowName());
                }
            } else {
                //不是我处理，还有后续的撤销
                endTime = getNextDate(comment.getId(), commentEntities);
                if (getNextNodeType(comment.getId(), commentEntities)) {
                    for (Comment otherComment : commentEntities) {
                        if (otherComment.getType().equals(Comment.NODE_TYPE)) {
                            circulationHistoryVo.setOperationType(comment.getFullMessage());
                        }
                    }
                } else {
                    return;
                }
                circulationHistoryVo.setEndTime(nodeDateFormat.format(getNextDate(comment.getId(), commentEntities)));
            }
        } else {
            if (isLastComment(comment, commentEntities)) {
                circulationHistoryVo.setOperationType(OperationTypeEnum.PROCESSING.getShowName());
                endTime = new Date();
            } else {
                //不是我处理，还有后续的撤销
                circulationHistoryVo.setEndTime(nodeDateFormat.format(getNextDate(comment.getId(), commentEntities)));
                circulationHistoryVo.setOperationType(OperationTypeEnum.CANCEL_DELEGATE.getShowName());
                for (Comment commentEntity : commentEntities) {
                    if (commentEntity.getType().equals(Comment.NODE_TYPE)) {
                        if (commentEntity instanceof CommentEntityImpl) {
                            circulationHistoryVo.setOperationType(((CommentEntityImpl) commentEntity).getMessage());
                        }
                    }
                }
                endTime = getNextDate(comment.getId(), commentEntities);
                return;
            }
        }
        commentVo.setEndTime(commentDateFormat.format(endTime));
        circulationHistoryVo.setState(CirculationHistoryVoStateEnum.NORMAL);
        circulationHistoryVo.setDurationInMillis(timeStampToDhms(endTime.getTime() - comment.getTime().getTime()));
        circulationHistoryVo.addComment(commentVo);
        if (hasCirculationInformation(historicTaskInstance.getId(), commentVo.getUserId())) {
            commentVo.setHasCirculationInformation(true);
            commentVo.setTaskId(historicTaskInstance.getId());
        }
        list.add(circulationHistoryVo);
    }

    public List<CommentVo> getComments(List<HistoricTaskInstance> tasks, HistoricTaskInstance task, CirculationHistoryVo circulationHistoryVo
            , String nodeComment, List<String> taskIds, BpmnModel bpmnModel) {
        List<String> assignees = new ArrayList<>();
        List<CommentVo> result = new ArrayList<>();
        //同一个任务
        Boolean isSameTask = false;
        Date endTime = null;
        Boolean isEnd = true;
        Date startTime = task.getStartTime();
        String executionId = task.getExecutionId();
        for (HistoricTaskInstance historicTaskInstance : tasks) {
            //多人需要合并，判断是否是一个任务
            if (bpmnModel.getFlowElement(historicTaskInstance.getTaskDefinitionKey()) instanceof KaiteMultiUserTask ||
                    bpmnModel.getFlowElement(historicTaskInstance.getTaskDefinitionKey()) instanceof KaiteCounterSignUserTask) {
                if (executionId.contains(historicTaskInstance.getExecutionId())) {
                    isSameTask = true;
                }
            }

            if (historicTaskInstance.getId().equals(task.getId())) {
                isSameTask = true;
            }
            if (isSameTask && !(historicTaskInstance.getTaskDefinitionKey().equals(task.getTaskDefinitionKey()))) {
                isSameTask = false;
            }
            if (isSameTask) {
                taskIds.add(historicTaskInstance.getId());
                String userName = workFlowUserService.getUserNameById(historicTaskInstance.getAssignee());
                CommentVo commentVo = new CommentVo();
                List<Comment> commentEntities = taskService.getTaskComments(historicTaskInstance.getId());

                if (commentEntities.size() == 0) {
                    if (hasCirculationInformation(historicTaskInstance.getId(), historicTaskInstance.getAssignee())) {
                        commentVo.setHasCirculationInformation(true);
                        commentVo.setTaskId(historicTaskInstance.getId());
                    }
                    commentVo.setUserName(userName);
                    commentVo.setUserId(historicTaskInstance.getAssignee());
                    commentVo.setUserAvatar(workFlowUserService.getUserPhotoById(historicTaskInstance.getAssignee()));
                    if (historicTaskInstance.getEndTime() != null) {
                        commentVo.setEndTime(commentDateFormat.format(historicTaskInstance.getEndTime()));
                    }
                    if (isEnd && historicTaskInstance.getEndTime() == null) {
                        isEnd = false;
                        endTime = null;
                    } else {
                        endTime = historicTaskInstance.getEndTime();
                    }
                    if (isEnd == false) {
                        endTime = null;
                    }
                    if (!Strings.isNullOrEmpty(commentVo.getUserId())) {
                        result.add(commentVo);
                        assignees.add(userName);
                    }
                    commentVo.setCommentType(OperationTypeEnum.NONE.getShowName());
                } else {
                    for (Comment comment : commentEntities) {
                        if (comment.getType().equals(Comment.USER_COMMENT)) {
                            commentVo.setComment(comment.getFullMessage());
                        } else if (comment.getType().equals(Comment.NODE_TYPE)) {
                            nodeComment = comment.getFullMessage();
                        } else if (comment.getType().equals(Comment.COUNTERSIGNED)) {
                            if (comment.getFullMessage().equals(ActivitiCountersignedOpinionType.PASS.toString())) {
                                commentVo.setCommentType(OperationTypeEnum.PASS.getShowName());
                            } else if (comment.getFullMessage().equals(ActivitiCountersignedOpinionType.VETO.toString())) {
                                commentVo.setCommentType(OperationTypeEnum.VETO.getShowName());
                            } else if (comment.getFullMessage().equals(ActivitiCountersignedOpinionType.ABSTAIN.toString())) {
                                commentVo.setCommentType(OperationTypeEnum.ABSTAIN.getShowName());
                            }
                        }
                    }
                    if (Strings.isNullOrEmpty(commentVo.getCommentType())) {
                        if (!Strings.isNullOrEmpty(commentVo.getComment())) {
                            commentVo.setCommentType(OperationTypeEnum.PROCESSED.getShowName());
                        } else {
                            commentVo.setCommentType(OperationTypeEnum.NONE.getShowName());
                        }
                    }
                    commentVo.setUserName(userName);
                    if (hasCirculationInformation(historicTaskInstance.getId(), historicTaskInstance.getAssignee())) {
                        commentVo.setHasCirculationInformation(true);
                        commentVo.setTaskId(historicTaskInstance.getId());
                    }
                    commentVo.setUserId(historicTaskInstance.getAssignee());
                    commentVo.setUserAvatar(workFlowUserService.getUserPhotoById(historicTaskInstance.getAssignee()));
                    if (historicTaskInstance.getEndTime() != null) {
                        commentVo.setEndTime(commentDateFormat.format(historicTaskInstance.getEndTime()));
                    }
                    if (isEnd && historicTaskInstance.getEndTime() == null) {
                        isEnd = false;
                        endTime = null;
                    } else {
                        endTime = historicTaskInstance.getEndTime();
                    }
                    if (isEnd == false) {
                        endTime = null;
                    }
                    if (!Strings.isNullOrEmpty(commentVo.getUserId())) {
                        result.add(commentVo);
                        assignees.add(userName);
                    }
                }
                if (DateUtils.getDate(circulationHistoryVo.getStartTime(), "yyyy-MM-dd HH:mm:ss.SSS").getTime() > historicTaskInstance.getStartTime().getTime()) {
                    circulationHistoryVo.setStartTime(nodeDateFormat.format(historicTaskInstance.getStartTime()));
                }
            }
        }


        FlowElement flowElement = bpmnModel.getMainProcess().getFlowElement(task.getTaskDefinitionKey());
        KaiteBaseUserTask kaiteBaseUserTask = (KaiteBaseUserTask) flowElement;
        if (kaiteBaseUserTask.getLoopCharacteristics() != null && kaiteBaseUserTask.getLoopCharacteristics().isSequential()) {
            endTime = buildSequentialVo(task, result, assignees, endTime);
        }

        if (flowElement instanceof KaiteCandidateUserTask && result.size() == 0) {
            buildCandidates(task, result, assignees);
        }

        if (!Strings.isNullOrEmpty(circulationHistoryVo.getTitle())) {
            getTitle(flowElement, circulationHistoryVo, result, task);
        }

        circulationHistoryVo.setAssignees(assignees);
        if (Strings.isNullOrEmpty(nodeComment) && endTime != null) {
            circulationHistoryVo.setOperationType(OperationTypeEnum.SUBMIT.getShowName());
        } else if (!Strings.isNullOrEmpty(nodeComment)) {
            circulationHistoryVo.setOperationType(nodeComment);
        } else {
            circulationHistoryVo.setOperationType(OperationTypeEnum.PROCESSING.getShowName());
        }

        if (endTime != null) {
            circulationHistoryVo.setEndTime(nodeDateFormat.format(endTime));
        }
        if (endTime == null) {
            endTime = new Date();
        }
        circulationHistoryVo.setDurationInMillis(timeStampToDhms(endTime.getTime() - startTime.getTime()));
        return result;
    }

    private Date buildSequentialVo(HistoricTaskInstance task, List<CommentVo> result, List<String> assignees, Date endTime) {
        Boolean isEnd = true;
        Object object = getLoopVariable(task.getExecutionId(), task.getProcessInstanceId(), "datas");
        if (object != null) {
            List<String> userIds = (List<String>) object;
            for (String userId : userIds) {
                Boolean isAdd = true;
                for (CommentVo vo : result) {
                    if (vo.getUserId().equals(userId)) {
                        isAdd = false;
                        break;
                    }
                }
                if (isAdd) {
                    CommentVo vo = new CommentVo();
                    vo.setUserId(userId);
                    String userName = workFlowUserService.getUserNameById(userId);
                    if (hasCirculationInformation(task.getId(), userId)) {
                        vo.setHasCirculationInformation(true);
                        vo.setTaskId(task.getId());
                    }
                    vo.setUserName(userName);
                    vo.setCommentType(OperationTypeEnum.NONE.getShowName());
                    isEnd = false;
                    result.add(vo);
                    assignees.add(userName);
                }
            }
        }
        if (!isEnd) {
            endTime = null;
        }
        return endTime;
    }

    private void buildCandidates(HistoricTaskInstance task, List<CommentVo> result, List<String> assignees) {
        List<HistoricIdentityLink> linkEntities = processEngine.getProcessEngineConfiguration().getHistoryService()
                .getHistoricIdentityLinksForTask(task.getId());
        for (HistoricIdentityLink identityLinkEntity : linkEntities) {
            if (identityLinkEntity.getType().equals(IdentityLinkType.CANDIDATE)) {
                CommentVo vo = new CommentVo();
                String userName = workFlowUserService.getUserNameById(identityLinkEntity.getUserId());
                vo.setUserId(identityLinkEntity.getUserId());
                if (hasCirculationInformation(task.getId(), vo.getUserId())) {
                    vo.setHasCirculationInformation(true);
                    vo.setTaskId(task.getId());
                }
                vo.setUserName(userName);
                vo.setCommentType(OperationTypeEnum.NONE.getShowName());
                result.add(vo);
                assignees.add(userName);
            }
        }
    }

    private void getTitle(FlowElement flowElement, CirculationHistoryVo circulationHistoryVo, List<CommentVo> result,
                          HistoricTaskInstance task) {
        if ((flowElement instanceof KaiteCounterSignUserTask)) {
            KaiteCounterSignUserTask kaiteCounterSignUserTask = (KaiteCounterSignUserTask) flowElement;
            if (kaiteCounterSignUserTask.getCounterSignConfig().getCounterSignPassType() == CounterSignPassTypeEnum.PASS_PRINCIPAL) {
                String executionId = task.getExecutionId();
                Object userIdObject = getLoopVariable(executionId, task.getProcessInstanceId(), MultiInstanceActivityBehavior.PASS_PRINCIPAL);
                String userId = (String) userIdObject;
                String comment = "";
                for (CommentVo vo : result) {
                    if (vo.getUserId().equals(userId) && !Strings.isNullOrEmpty(vo.getCommentType())) {
                        comment = vo.getCommentType();
                    }
                }
                String userName = workFlowUserService.getUserNameById(userId);
                circulationHistoryVo.setTitle(circulationHistoryVo.getTitle().replace(MultiInstanceActivityBehavior.PASS_PRINCIPAL, userName));
                if (Strings.isNullOrEmpty(comment)) {
                    circulationHistoryVo.setTitle(circulationHistoryVo.getTitle().replace("%s", "未处理"));
                } else {
                    circulationHistoryVo.setTitle(circulationHistoryVo.getTitle().replace("%s", comment));
                }
            } else {
                int passConut = 0;
                int abstention = 0;
                int count = result.size();
                for (CommentVo vo : result) {
                    if (!Strings.isNullOrEmpty(vo.getCommentType())) {
                        if ("通过".equals(vo.getCommentType())) {
                            passConut++;
                        }
                        if ("弃权".equals(vo.getCommentType())) {
                            abstention++;
                        }
                    }
                }

                if (0 != abstention) {
                    count = count - abstention;
                }
                String ratio = getRatio(Double.valueOf(passConut), Double.valueOf(count));
                String title = circulationHistoryVo.getTitle();
                title = title.replace("%s", ratio);
                circulationHistoryVo.setTitle(title);
            }
        }
    }

    protected Object getLoopVariable(String executionId, String piid, String name) {
        HistoricVariableInstanceQuery historicVariableInstanceQuery = processEngine.getProcessEngineConfiguration().getHistoryService()
                .createHistoricVariableInstanceQuery();
        ListIterator<HistoricVariableInstance> list = historicVariableInstanceQuery.
                executionId(executionId).processInstanceId(piid).variableName(name).orderBy
                (new HistoricVariableInstanceQueryProperty("CREATE_TIME_")).desc().
                listPage(0, 30).listIterator();
        for (ListIterator<HistoricVariableInstance> it = list; it.hasNext(); ) {
            HistoricVariableInstance historicVariableInstance = it.next();
            return historicVariableInstance.getValue();
        }
        return null;
    }

    private String getRatio(Double d1, Double d2) {
        if (d1 == null || d2 == null || d2 <= 0) {
            return "0%";
        }
        NumberFormat percent = NumberFormat.getPercentInstance();
        percent.setMaximumFractionDigits(0);
        percent.setMinimumFractionDigits(0);
        return percent.format(d1 / d2);
    }

    protected String getCountersignedTitle(String nodeId, BpmnModel bpmnModel) {
        String styleSpanStart = "<span style=\"color:#D8001B;\">";
        String styleSpanEnd = "</span>";
        FlowElement flowElement = bpmnModel.getMainProcess().getFlowElement(nodeId);
        if (!(flowElement instanceof KaiteCounterSignUserTask)) {
            return "";
        }
        KaiteCounterSignUserTask kaiteCounterSignUserTask = (KaiteCounterSignUserTask) flowElement;
        String title = "";
        String typeName = kaiteCounterSignUserTask.getCounterSignConfig().getCounterSignPassType().getName();
        String sequentialName = "并行审批";
        if (kaiteCounterSignUserTask.getLoopCharacteristics().isSequential()) {
            sequentialName = "顺序审批";
        }
        if (kaiteCounterSignUserTask.getCounterSignConfig().getCounterSignPassType() == CounterSignPassTypeEnum.PASS_PRINCIPAL) {
            title = String.format("会签采用%s【%s-%s】%s，负责人为：%s【%s】%s，当前处理状态为：%s【%s】%s", styleSpanStart, typeName, sequentialName, styleSpanEnd, styleSpanStart,
                    MultiInstanceActivityBehavior.PASS_PRINCIPAL, styleSpanEnd, styleSpanStart, "%s", styleSpanEnd);
        } else {
            title = String.format("会签采用%s【%s-%s】%s，比例标准为：%s【%s】%s，当前通过比例为：%s【%s】%s", styleSpanStart, typeName, sequentialName, styleSpanEnd, styleSpanStart,
                    kaiteCounterSignUserTask.getCounterSignConfig().getAmount() + "%", styleSpanEnd, styleSpanStart, "%s", styleSpanEnd);
        }
        return title;
    }

    public static String timeStampToDhms(long milliseconds) {
        long seconds = TimeUnit.MILLISECONDS.toSeconds(milliseconds) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(milliseconds));
        if (seconds == 0) {
            seconds = 1;
        }

        long minutes = TimeUnit.MILLISECONDS.toMinutes(milliseconds) - TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(milliseconds));

        long hours = TimeUnit.MILLISECONDS.toHours(milliseconds) - TimeUnit.DAYS.toHours(TimeUnit.MILLISECONDS.toDays(milliseconds));

        long day = TimeUnit.MILLISECONDS.toDays(milliseconds);

        if (day > 0) {
            return day + "天";
        }

        if (hours > 0) {
            return hours + "小时";
        }

        if (minutes > 0) {
            return minutes + "分钟";
        }

        return seconds + "秒";

    }

    private Date getNextDate(String id, List<Comment> commentEntities) {
        int i = 0;
        for (Comment comment : commentEntities) {
            if (comment.getType().equals(Comment.NODE_TYPE)) {
                continue;
            }
            if (id.equals(comment.getId())) {
                break;
            }
            i++;
        }
        if (i + 1 == commentEntities.size()) {
            return new Date();
        }
        return commentEntities.get(i).getTime();
    }

    private Boolean getNextNodeType(String id, List<Comment> commentEntities) {
        int i = 0;
        for (Comment comment : commentEntities) {
            if (id.equals(comment.getId())) {
                break;
            }
            i++;
        }
        if (commentEntities.get(i).getType().equals(Comment.NODE_TYPE)) {
            return true;
        } else {
            return false;
        }
    }

    private Boolean isLastComment(Comment comment, List<Comment> commentEntities) {
        int i = 0;
        for (Comment comment1 : commentEntities) {
            if (comment.getId().equals(comment1.getId())) {
                break;
            }
            i++;
        }

        if (i + 1 == commentEntities.size()) {
            return true;
        } else if (commentEntities.get(i + 1).getType().equals(Comment.USER_COMMENT)) {
            return true;
        } else {
            return false;
        }
    }

}
